// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/metrics/histogram_base.h"

#include <limits.h>

#include <memory>
#include <utility>

#include "base/json/json_string_value_serializer.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_samples.h"
#include "base/metrics/sparse_histogram.h"
#include "base/metrics/statistics_recorder.h"
#include "base/pickle.h"
#include "base/process/process_handle.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"

namespace base {

std::string HistogramTypeToString(HistogramType type)
{
    switch (type) {
    case HISTOGRAM:
        return "HISTOGRAM";
    case LINEAR_HISTOGRAM:
        return "LINEAR_HISTOGRAM";
    case BOOLEAN_HISTOGRAM:
        return "BOOLEAN_HISTOGRAM";
    case CUSTOM_HISTOGRAM:
        return "CUSTOM_HISTOGRAM";
    case SPARSE_HISTOGRAM:
        return "SPARSE_HISTOGRAM";
    }
    NOTREACHED();
    return "UNKNOWN";
}

HistogramBase* DeserializeHistogramInfo(PickleIterator* iter)
{
    //   int type;
    //   if (!iter->ReadInt(&type))
    //     return NULL;
    //
    //   switch (type) {
    //     case HISTOGRAM:
    //       return Histogram::DeserializeInfoImpl(iter);
    //     case LINEAR_HISTOGRAM:
    //       return LinearHistogram::DeserializeInfoImpl(iter);
    //     case BOOLEAN_HISTOGRAM:
    //       return BooleanHistogram::DeserializeInfoImpl(iter);
    //     case CUSTOM_HISTOGRAM:
    //       return CustomHistogram::DeserializeInfoImpl(iter);
    //     case SPARSE_HISTOGRAM:
    //       return SparseHistogram::DeserializeInfoImpl(iter);
    //     default:
    //       return NULL;
    //   }
    DebugBreak();
    return nullptr;
}

const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX;
HistogramBase* HistogramBase::report_histogram_ = nullptr;

HistogramBase::HistogramBase(const std::string& name)
    : histogram_name_(name)
    , flags_(kNoFlags)
{
}

HistogramBase::~HistogramBase() { }

void HistogramBase::CheckName(const StringPiece& name) const
{
    DCHECK_EQ(histogram_name(), name);
}

void HistogramBase::SetFlags(int32_t flags)
{
    HistogramBase::Count old_flags = subtle::NoBarrier_Load(&flags_);
    subtle::NoBarrier_Store(&flags_, old_flags | flags);
}

void HistogramBase::ClearFlags(int32_t flags)
{
    HistogramBase::Count old_flags = subtle::NoBarrier_Load(&flags_);
    subtle::NoBarrier_Store(&flags_, old_flags & ~flags);
}

void HistogramBase::AddTime(const TimeDelta& time)
{
    Add(static_cast<Sample>(time.InMilliseconds()));
}

void HistogramBase::AddBoolean(bool value)
{
    Add(value ? 1 : 0);
}

bool HistogramBase::SerializeInfo(Pickle* pickle) const
{
    if (!pickle->WriteInt(GetHistogramType()))
        return false;
    return SerializeInfoImpl(pickle);
}

uint32_t HistogramBase::FindCorruption(const HistogramSamples& samples) const
{
    // Not supported by default.
    return NO_INCONSISTENCIES;
}

void HistogramBase::WriteJSON(std::string* output) const
{
    Count count;
    int64_t sum;
    std::unique_ptr<ListValue> buckets(new ListValue());
    GetCountAndBucketData(&count, &sum, buckets.get());
    std::unique_ptr<DictionaryValue> parameters(new DictionaryValue());
    GetParameters(parameters.get());

    JSONStringValueSerializer serializer(output);
    DictionaryValue root;
    root.SetString("name", histogram_name());
    root.SetInteger("count", count);
    root.SetDouble("sum", static_cast<double>(sum));
    root.SetInteger("flags", flags());
    root.Set("params", std::move(parameters));
    root.Set("buckets", std::move(buckets));
    root.SetInteger("pid", GetCurrentProcId());
    serializer.Serialize(root);
}

// static
void HistogramBase::EnableActivityReportHistogram(
    const std::string& process_type)
{
    //   DCHECK(!report_histogram_);
    //   size_t existing = StatisticsRecorder::GetHistogramCount();
    //   if (existing != 0) {
    //     DVLOG(1) << existing
    //              << " histograms were created before reporting was enabled.";
    //   }
    //
    //   std::string name =
    //       "UMA.Histograms.Activity" +
    //       (process_type.empty() ? process_type : "." + process_type);
    //
    //   // Calling FactoryGet() here rather than using a histogram-macro works
    //   // around some problems with tests that could end up seeing the results
    //   // histogram when not expected due to a bad interaction between
    //   // HistogramTester and StatisticsRecorder.
    //   report_histogram_ = LinearHistogram::FactoryGet(
    //       name, 1, HISTOGRAM_REPORT_MAX, HISTOGRAM_REPORT_MAX + 1,
    //       kUmaTargetedHistogramFlag);
    //   report_histogram_->Add(HISTOGRAM_REPORT_CREATED);
    DebugBreak();
    return;
}

void HistogramBase::FindAndRunCallback(HistogramBase::Sample sample) const
{
    if ((flags() & kCallbackExists) == 0)
        return;

    StatisticsRecorder::OnSampleCallback cb = StatisticsRecorder::FindCallback(histogram_name());
    if (!cb.is_null())
        cb.Run(sample);
}

void HistogramBase::WriteAsciiBucketGraph(double current_size,
    double max_size,
    std::string* output) const
{
    const int k_line_length = 72; // Maximal horizontal width of graph.
    int x_count = static_cast<int>(k_line_length * (current_size / max_size)
        + 0.5);
    int x_remainder = k_line_length - x_count;

    while (0 < x_count--)
        output->append("-");
    output->append("O");
    while (0 < x_remainder--)
        output->append(" ");
}

const std::string HistogramBase::GetSimpleAsciiBucketRange(
    Sample sample) const
{
    std::string result;
    if (kHexRangePrintingFlag & flags())
        StringAppendF(&result, "%#x", sample);
    else
        StringAppendF(&result, "%d", sample);
    return result;
}

void HistogramBase::WriteAsciiBucketValue(Count current,
    double scaled_sum,
    std::string* output) const
{
    StringAppendF(output, " (%d = %3.1f%%)", current, current / scaled_sum);
}

// static
void HistogramBase::ReportHistogramActivity(const HistogramBase& histogram,
    ReportActivity activity)
{
    if (!report_histogram_)
        return;

    const int32_t flags = histogram.flags_;
    HistogramReport report_type = HISTOGRAM_REPORT_MAX;
    switch (activity) {
    case HISTOGRAM_CREATED:
        report_histogram_->Add(HISTOGRAM_REPORT_HISTOGRAM_CREATED);
        switch (histogram.GetHistogramType()) {
        case HISTOGRAM:
            report_type = HISTOGRAM_REPORT_TYPE_LOGARITHMIC;
            break;
        case LINEAR_HISTOGRAM:
            report_type = HISTOGRAM_REPORT_TYPE_LINEAR;
            break;
        case BOOLEAN_HISTOGRAM:
            report_type = HISTOGRAM_REPORT_TYPE_BOOLEAN;
            break;
        case CUSTOM_HISTOGRAM:
            report_type = HISTOGRAM_REPORT_TYPE_CUSTOM;
            break;
        case SPARSE_HISTOGRAM:
            report_type = HISTOGRAM_REPORT_TYPE_SPARSE;
            break;
        }
        report_histogram_->Add(report_type);
        if (flags & kIsPersistent)
            report_histogram_->Add(HISTOGRAM_REPORT_FLAG_PERSISTENT);
        if ((flags & kUmaStabilityHistogramFlag) == kUmaStabilityHistogramFlag)
            report_histogram_->Add(HISTOGRAM_REPORT_FLAG_UMA_STABILITY);
        else if (flags & kUmaTargetedHistogramFlag)
            report_histogram_->Add(HISTOGRAM_REPORT_FLAG_UMA_TARGETED);
        break;

    case HISTOGRAM_LOOKUP:
        report_histogram_->Add(HISTOGRAM_REPORT_HISTOGRAM_LOOKUP);
        break;
    }
}

} // namespace base
